home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 September / Macworld (1998-09).dmg / Shareware World / Info / For Developers / MacZoop 1.8.3 / Required Classes / Z Sources / CursorUtilities.c next >
Text File  |  1998-07-09  |  13KB  |  505 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *        CursorUtilities.c            - cursor stuff- spinning watch, etc
  5. *
  6. *        26/9/94        ©1994, Graham Cox
  7. *
  8. *        This code based on "Macintosh Programming Secrets" 2nd Edition by Keith Rollin & Scott K
  9. *
  10. *************************************************************************************************/
  11.  
  12. #include    "CursorUtilities.h"
  13.  
  14. #include    <LowMem.h>
  15. #include    <QDOffscreen.h>
  16.  
  17. // globals:
  18.  
  19. static short            gVBLCount;
  20. static VBLTaskWithA5Ptr    gCursorTask = NULL;
  21. static acurHdl            gWatchSpinner = NULL;
  22. static acurHdl            gBallSpinner = NULL;
  23. static acurHdl            gBusyArrowSpinner = NULL;
  24. static CCrsrHandle        gColourCursor = NULL;
  25. static Boolean            gCursorPaused = FALSE;
  26. static long                gPauseTicks;
  27. static Point            gMouseTrack;
  28.  
  29. short                    gLastCursorID = -1;
  30.  
  31. // macro for doing dodgy 68000 stuff!
  32.  
  33. #ifndef __powerc
  34.     VBLTaskWithA5Ptr    GetVBLRec(void) = 0x2008;
  35. #endif
  36.  
  37. static Handle    GetDetachedResource( ResType aType, short anID );
  38.  
  39. /*-------------------------***  GETDETACHEDRESOURCE  ***--------------------------------*/
  40. /*    
  41.  
  42. returns a detched resourse with type and ID passed.
  43.  
  44. ----------------------------------------------------------------------------------------*/
  45.  
  46. static Handle    GetDetachedResource( ResType aType, short anID )
  47. {
  48.     Handle    temp = NULL;
  49.     
  50.     temp = GetResource( aType, anID );
  51.     
  52.     if ( temp )
  53.         DetachResource( temp );
  54.  
  55.     return temp;
  56. }
  57.  
  58. /*--------------------------***  INITANIMATEDCURSOR  ***--------------------------------*/
  59. /*    
  60.  
  61. sets up an animated cursor by reading the 'acur' resource with the ID passed.
  62.  
  63. ----------------------------------------------------------------------------------------*/
  64.  
  65. acurHdl        InitAnimatedCursor( short resID )
  66. {
  67.     acurHdl        temp;
  68.     short        ccount;
  69.     CursHandle    aCursor;
  70.     CursHandle*    workPtr;
  71.     
  72.     temp = (acurHdl) GetDetachedResource( 'acur', resID );
  73.     
  74.     if (temp)
  75.     {
  76.         HNoPurge((Handle) temp );
  77.         
  78.         ccount = (*temp)->numCursors;
  79.         (*temp)->numCursors *= 32;
  80.         (*temp)->index = 0;
  81.         
  82.         HLock((Handle) temp);
  83.         workPtr = (*temp)->cursors;
  84.         
  85.         while( ccount-- )
  86.         {
  87.             aCursor = (CursHandle) GetDetachedResource( 'CURS', *(short*) workPtr );
  88.             HNoPurge((Handle) aCursor );
  89.             *workPtr++ = aCursor; 
  90.         }    
  91.         HUnlock((Handle) temp );
  92.     }
  93.     
  94.     return temp;
  95. }
  96.  
  97.  
  98.  
  99. /*-------------------------***  STARTCURSORANIMATION  ***-------------------------------*/
  100. /*    
  101. sets up the VBL queue to animate the cursor passed.
  102.  
  103. ----------------------------------------------------------------------------------------*/
  104.  
  105. void        StartCursorAnimation( short period, acurHdl theCursor )
  106. {
  107.     // primes the VBL queue to spin the cursor automatically
  108.     
  109.     VBLUPP        vTaskUPP;
  110.     
  111.     if (theCursor)
  112.     {
  113.         LockCursorData(theCursor);
  114.     
  115.         gVBLCount = period;
  116.         gCursorTask = (VBLTaskWithA5Ptr) NewPtr( sizeof( VBLTaskWithA5 ));
  117.         
  118.         gCursorTask->theTask.qType = vType;
  119.         
  120.         vTaskUPP = NewVBLProc((ProcPtr) VBLCursorSpin );
  121.         
  122.         gCursorTask->theTask.vblAddr = vTaskUPP;
  123.         gCursorTask->theTask.vblCount = period;
  124.         gCursorTask->theTask.vblPhase = 0;
  125.         gCursorTask->A5 = (long) LMGetCurrentA5();
  126.         gCursorTask->theCursor = theCursor;
  127.         
  128.         (void) VInstall((QElemPtr) gCursorTask );
  129.         
  130.         gCursorPaused = FALSE;
  131.     }
  132. }
  133.  
  134.  
  135. /*-------------------------***  STOPCURSORANIMATION  ***--------------------------------*/
  136. /*    
  137.  
  138. Kills the current cursor in the VBL queue and unlocks it. This then restores the arrow.
  139. ----------------------------------------------------------------------------------------*/
  140.  
  141. void        StopCursorAnimation()
  142. {
  143.     // removes spinning cursor task from the VBL queue
  144.     
  145.     acurHdl        theCursor;
  146.     
  147.     if ( gCursorTask )
  148.     {
  149.         (void) VRemove((QElemPtr) gCursorTask );
  150.         
  151.         theCursor = gCursorTask->theCursor;
  152.         DisposeRoutineDescriptor( gCursorTask->theTask.vblAddr );
  153.         
  154.         DisposePtr((Ptr) gCursorTask);
  155.         gCursorTask = NULL;
  156.         UnlockCursorData( theCursor );
  157.         
  158.         // force switch to default cursor:
  159.         
  160.         gLastCursorID = -1;
  161.         SetCursorShape( 0 );
  162.     }
  163. }
  164.  
  165.  
  166. /*------------------------***  PAUSECURSORANIMATION  ***--------------------------------*/
  167. /*    
  168.  
  169. Pauses the current animating cursor, returning the cursor to the shape passed. If the
  170. cursor is not animating, this does nothing. The animation can be restarted with a call
  171. to ResumeCursorAnimation().
  172. ----------------------------------------------------------------------------------------*/
  173.  
  174. void        PauseCursorAnimation( short tempFixedCursorID )
  175. {
  176.     if ( CursorAnimating())
  177.     {
  178.         gCursorPaused = TRUE;
  179.         gLastCursorID = -1;
  180.         gPauseTicks = 0;
  181.     }
  182.     SetCursorShape( tempFixedCursorID );
  183. }
  184.  
  185.  
  186.  
  187. /*------------------------***  RESUMECURSORANIMATION  ***-------------------------------*/
  188. /*    
  189. Restarts a paused animating cursor, if there is one, otherwise does nothing but reset the
  190. flag.
  191. ----------------------------------------------------------------------------------------*/
  192.  
  193. void        ResumeCursorAnimation()
  194. {
  195.     gCursorPaused = FALSE;
  196. }
  197.  
  198.  
  199. /*-----------------------------***  ANIMATECURSOR  ***----------------------------------*/
  200. /*    
  201.  
  202. Can be called repeatedly to animate a cursor. However, it is simpler to let the VBL task
  203. do this in general.
  204.  
  205. ----------------------------------------------------------------------------------------*/
  206.  
  207. void        AnimateCursor( short increment, acurHdl theCursor )
  208. {
  209.     // used only for non-VBL cursor spinning
  210.     
  211.     short        oldIndex,newIndex;
  212.     CursHandle    aCursor;
  213.     
  214.     if ( theCursor && !gCursorPaused )
  215.     {
  216.         oldIndex = (*theCursor)->index / 32;
  217.         
  218.         (*theCursor)->index += increment;
  219.         (*theCursor)->index %= (*theCursor)->numCursors;
  220.         
  221.         newIndex = (*theCursor)->index / 32;
  222.         
  223.         if (newIndex != oldIndex)
  224.         {
  225.             aCursor = (*theCursor)->cursors[newIndex];
  226.             SetCursor(*aCursor);
  227.         }
  228.     }
  229. }
  230.  
  231.  
  232. /*----------------------------***  LOCKCURSORDATA  ***----------------------------------*/
  233. /*    
  234.  
  235. locks down the cursor handles ready for animation
  236.  
  237. ----------------------------------------------------------------------------------------*/
  238.  
  239. void        LockCursorData( acurHdl theCursor )
  240. {
  241.     // locks the cursor and its internal data
  242.     
  243.     short        ccount;
  244.     CursHandle    *workPtr;
  245.     
  246.     ccount = (*theCursor)->numCursors / 32;
  247.     
  248.     HLockHi((Handle) theCursor);
  249.     workPtr = (*theCursor)->cursors;
  250.     
  251.     while (ccount--)
  252.         HLockHi((Handle) *workPtr++);    
  253. }
  254.  
  255.  
  256. /*--------------------------***  UNLOCKCURSORDATA  ***----------------------------------*/
  257. /*    
  258.  
  259. unlocks the cursor handles
  260. ----------------------------------------------------------------------------------------*/
  261.  
  262. void        UnlockCursorData( acurHdl theCursor )
  263. {
  264.     // unlocks the cursor
  265.     
  266.     short        ccount;
  267.     CursHandle    *workPtr;
  268.     
  269.     ccount = (*theCursor)->numCursors / 32;
  270.     workPtr = (*theCursor)->cursors;
  271.     
  272.     while (ccount--)
  273.         HUnlock((Handle) *workPtr++);    
  274.  
  275.     HUnlock((Handle) theCursor);
  276. }
  277.  
  278. /*-----------------------------***  VBLCURSORSPIN  ***----------------------------------*/
  279. /*    
  280.  
  281. this is the VBL callback that actually performs the animation of a VBL-driven cursor
  282. ----------------------------------------------------------------------------------------*/
  283.  
  284. #ifndef __powerc
  285.     void        VBLCursorSpin()
  286. #else
  287.     void        VBLCursorSpin(VBLTaskWithA5Ptr theTask)
  288. #endif
  289. {
  290.     // this is the function that is called from the interrupt task. 
  291.     
  292.     acurHdl        theCrsr;
  293.     long        oldA5;
  294.     
  295. #ifndef __powerc
  296.     VBLTaskWithA5Ptr    theTask;
  297.     
  298.     theTask = GetVBLRec();
  299.     oldA5 = SetA5(theTask->A5);
  300.     if (LMGetCrsrBusy() == 0)
  301.     {
  302.         theCrsr = theTask->theCursor;
  303.         AnimateCursor( 32, theCrsr );
  304.     }
  305.     theTask->theTask.vblCount = gVBLCount;
  306.     (void) SetA5( oldA5 );
  307.     
  308. #else
  309.     oldA5 = SetA5( theTask->A5 );
  310.         
  311.     if (LMGetCrsrBusy() == 0)
  312.     {
  313.         theCrsr = theTask->theCursor;
  314.         AnimateCursor( 32, theCrsr );
  315.     }
  316.     theTask->theTask.vblCount = gVBLCount;
  317.     (void) SetA5( oldA5 );
  318. #endif
  319. }
  320.  
  321.  
  322. /*-----------------------------***  SETWATCHCURSOR  ***---------------------------------*/
  323. /*    
  324.  
  325. starts the animated watch cursor. To stop it, call StopCursorAnimation.
  326. ----------------------------------------------------------------------------------------*/
  327.  
  328. void        SetWatchCursor()
  329. {
  330.     // HL call installs VBL spinner for watch, which should have been inited beforehand
  331.     
  332.     if ( gCursorTask )
  333.         StopCursorAnimation();
  334.     
  335.     if ( gWatchSpinner && ! gCursorTask )
  336.         StartCursorAnimation( 8, gWatchSpinner );
  337. }
  338.  
  339. /*---------------------------***  SETBEACHBALLCURSOR  ***-------------------------------*/
  340. /*    
  341.  
  342. starts the animated "beachball" cursor. To stop it, call StopCursorAnimation.
  343. ----------------------------------------------------------------------------------------*/
  344.  
  345. void        SetBeachBallCursor()
  346. {
  347.     if ( gCursorTask )
  348.         StopCursorAnimation();
  349.     
  350.     if ( gBallSpinner && ! gCursorTask )
  351.         StartCursorAnimation( 3, gBallSpinner );
  352.  
  353. }
  354.  
  355.  
  356. void        SetBusyArrowCursor()
  357. {
  358.     if ( gCursorTask )
  359.         StopCursorAnimation();
  360.     
  361.     if ( gBallSpinner && ! gCursorTask )
  362.         StartCursorAnimation( 3, gBusyArrowSpinner );
  363. }
  364.  
  365. /*-------------------------------***  GETMODIFIERS  ***---------------------------------*/
  366. /*    
  367.  
  368. can be called at any time to get the current state of the keyboard modifier keys. Useful
  369. if the cursor is changed with different modifiers down.
  370. ----------------------------------------------------------------------------------------*/
  371.  
  372. short        GetModifiers()
  373. {
  374.     // returns the current state of the modifier keys as if it was the modifier field in
  375.     // an event record. This can be called at any time, even when an event is not available.
  376.     // It is very useful for updating the cursor to show the modifier state.
  377.     
  378.     unsigned char    theKeys[16];
  379.     short            modifiers;
  380.     
  381.     
  382.     GetKeys((UInt32*) theKeys);
  383.     
  384.     modifiers = 0;
  385.     
  386.     ((theKeys[0x37 >> 3] >> (0x37 & 7)) & 1)? modifiers |= cmdKey : 0;
  387.     ((theKeys[0x38 >> 3] >> (0x38 & 7)) & 1)? modifiers |= shiftKey : 0;
  388.     ((theKeys[0x39 >> 3] >> (0x39 & 7)) & 1)? modifiers |= alphaLock : 0;
  389.     ((theKeys[0x3A >> 3] >> (0x3A & 7)) & 1)? modifiers |= optionKey : 0;
  390.     ((theKeys[0x3B >> 3] >> (0x3B & 7)) & 1)? modifiers |= controlKey : 0;
  391.  
  392.     (! Button())? modifiers |= btnState : 0;
  393.     
  394.     return modifiers;
  395. }
  396.  
  397.  
  398. /*------------------------------***  APPCURSORINIT  ***---------------------------------*/
  399. /*    
  400. high-level call to initialise the two animated cursors. Extend this for others you may have
  401. ----------------------------------------------------------------------------------------*/
  402.  
  403. void        AppCursorInit()
  404. {
  405.     // called once at application start-up to initialise the animated cursor globals.
  406.  
  407.     gWatchSpinner = InitAnimatedCursor( kWatchResID );
  408.     gBallSpinner = InitAnimatedCursor( kBeachBallResID );
  409.     gBusyArrowSpinner = InitAnimatedCursor( kBusyArrowResID );
  410.     
  411.     gLastCursorID = -1;
  412.     gColourCursor = NULL;
  413. }
  414.  
  415. /*------------------------------***  CURSORANIMATING  ***-------------------------------*/
  416. /*    
  417. returns TRUE if an animated cursor is on screen. You should not call
  418. SetCursor if this is true, or ugly cursor flickering occurs.
  419. ----------------------------------------------------------------------------------------*/
  420.  
  421. Boolean        CursorAnimating()
  422. {
  423.     return (( gCursorTask != NULL ) && !gCursorPaused );
  424. }
  425.  
  426.  
  427.  
  428. Boolean        CursorAnimationVBLRunning()
  429. {
  430.     return ( gCursorTask != NULL );
  431. }
  432.  
  433.  
  434. /*-------------------------------***  SETCURSORSHAPE  ***-------------------------------*/
  435. /*
  436. You can use this instead of GetCursor/SetCursor to set the cursor shape. This also will
  437. correctly manage a colour cursor if there is one available with the same ID. This just
  438. works without any special coding, optimising for loading the colour cursor minimally.
  439. If you pass 0 it will set the arrow. This method is unsuitable for VBL animated cursors.    
  440. ----------------------------------------------------------------------------------------*/
  441.  
  442. void        SetCursorShape( short resID )
  443. {
  444.     if (( resID != gLastCursorID ) && ! CursorAnimating())
  445.     {
  446.         CCrsrHandle        ncH = NULL;
  447.             
  448.         gLastCursorID = resID;
  449.             
  450.         if ( resID == 0 )
  451.             SetCursor( &qd.arrow );
  452.         else
  453.         {
  454.             // see if there's a colour cursor with that ID. We do not look for colour
  455.             // cursors with IDs less than 128 since for some reason Inside Mac says
  456.             // this is not allowed. Can't really see why this is!
  457.             
  458.             if ( resID > 127 )
  459.             {
  460.                 ncH = GetCCursor( resID );
  461.                 
  462.                 if ( ncH )
  463.                 {
  464.                     // due to a subtle and undocumented side-effect of SetCCursor, we must be
  465.                     // careful to preserve the GDevice around this call, or bad things can
  466.                     // happen.
  467.                     
  468.                     GDHandle    saveDevice;
  469.                     
  470.                     saveDevice = GetGDevice();
  471.                     
  472.                     SetCCursor( ncH );
  473.                     SetGDevice( saveDevice );
  474.                 }
  475.             }
  476.             
  477.             // if no colour cursor, use a black and white one. This works with all
  478.             // cursor IDs, including the standard iBeamCursor, crossCursor, etc.
  479.             
  480.             if ( ncH == NULL )
  481.             {
  482.                 CursHandle    cH;
  483.                 
  484.                 cH = GetCursor( resID );
  485.         
  486.                 if ( cH )
  487.                 {
  488.                     HLockHi((Handle) cH );
  489.                     SetCursor( *cH );
  490.                     HUnlock((Handle) cH );
  491.                 }
  492.             }
  493.         }    
  494.         
  495.         // dispose any old colour cursor
  496.         
  497.         if ( gColourCursor )
  498.             DisposeCCursor( gColourCursor );
  499.         
  500.         // keep track of new one or NULL
  501.         
  502.         gColourCursor = ncH;
  503.     }
  504. }
  505.